package com.nakamachizu.controller.mixi;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import net.arnx.jsonic.JSON;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.slim3.controller.Controller;
import org.slim3.controller.Navigation;
import org.slim3.datastore.Datastore;
import org.slim3.util.StringUtil;
import org.slim3.util.TimeZoneLocator;
import org.w3c.dom.Document;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery.TooManyResultsException;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.nakamachizu.constant.AppConstant;
import com.nakamachizu.constant.MixiConstant;
import com.nakamachizu.meta.MapDataMeta;
import com.nakamachizu.meta.session.MixiSessionDataMeta;
import com.nakamachizu.model.MapData;
import com.nakamachizu.model.mixi.PersonFriendModel;
import com.nakamachizu.model.mixi.PersonFriendsModel;
import com.nakamachizu.model.session.MixiSessionData;
import com.nakamachizu.service.XmlGenService;
import com.nakamachizu.service.mixi.RestfulService;
import com.nakamachizu.service.mixi.SessionService;
import com.nakamachizu.service.mixi.ValidatingSignedRequestsService;

public class LoginController extends Controller {

    private Logger log = Logger.getLogger(LoginController.class.getName());
    private String opensocial_app_id;
    private String opensocial_owner_id;
    private String opensocial_viewer_id;
    private MapDataMeta mapMeta = MapDataMeta.get();
    
    @Override
    public Navigation run() throws Exception {
        
        ValidatingSignedRequestsService service = new ValidatingSignedRequestsService();
        Map<String,String> param = service.verifyFetch(request);
        opensocial_app_id = requestScope("opensocial_app_id");
        opensocial_owner_id = requestScope("opensocial_owner_id");
        opensocial_viewer_id = requestScope("opensocial_viewer_id");

        if(!MixiConstant.MIXI_APP_ID().equals(opensocial_app_id)){
            throw new Exception("invalid app id");
        }
        
        String mapKey = "";
        String mixiSessionKey = "";
        
        if(!StringUtil.isEmpty(opensocial_owner_id)){
            MapData map = Datastore.query(mapMeta).filter(mapMeta.adminMailAddress.equal(opensocial_owner_id + MixiConstant.MIXI_MAIL_ADDRESS())).asSingle();
            MixiSessionDataMeta meta = MixiSessionDataMeta.get();
            MixiSessionData session = null;
            try{
                session = Datastore.query(meta).filter
                (
                    meta.ownerId.equal(opensocial_owner_id),
                    meta.viewerId.equal(opensocial_viewer_id),
                    meta.appId.equal(opensocial_app_id)
                    
                )
                .asSingle();
            }catch(TooManyResultsException e){
                List<Key> list = Datastore.query(meta).filter
                (
                    meta.ownerId.equal(opensocial_owner_id),
                    meta.viewerId.equal(opensocial_viewer_id),
                    meta.appId.equal(opensocial_app_id)
                    
                ).asKeyList();
                Datastore.delete(list);
                session = new MixiSessionData();
            }
            
            if(session == null){
                session = new MixiSessionData();
            }
            
            if(map != null){
                if(!AppConstant.ACCESSLEVEL_ALL().equals(map.getAccessLevel()) && !opensocial_owner_id.equals(opensocial_viewer_id)){
                    RestfulService rfs = new RestfulService();
                    rfs.makeSignature(opensocial_viewer_id,
                        RestfulService.TARGET_UID_ME,
                        "@friends/" + opensocial_owner_id,
                        RestfulService.FORMAT_JSON,
                        RestfulService.DEFAULT_COUNT,
                        RestfulService.DEFAULT_START_INDEX);
                    if(rfs.request() != 200){
                        StringBuilder buf = new StringBuilder("HTTP Headers\n");
                        for(HTTPHeader header : rfs.getHttpHeader()){
                            buf.append(header.getName()).append(":").append(header.getValue()).append("\n");
                        }
                        log.info("RequestUrl:" + rfs.getRequestUrl());
                        log.info(buf.toString());
                        log.info(rfs.getContents());
                        session.setFriend(false);
                    }else{
                        PersonFriendModel model = JSON.decode(rfs.getContents(), PersonFriendModel.class);
                        if(model.getTotalResults() == 1){
                            session.setOwnerDisplayName(model.getEntry().get(PersonFriendsModel.DISPLAY_NAME));
                            mapKey = map.getAccessKey();
                            session.setMapKey(map.getAccessKey());
                            session.setFriend(true);
                        }else{
                            throw new Exception("bad total results = " + Integer.toString(model.getTotalResults()));
                        }
                    }
                }else{
                    mapKey = map.getAccessKey();
                    session.setMapKey(map.getAccessKey());
                }
            }
            SessionService ss = new SessionService();
            session.setOwnerId(opensocial_owner_id);
            session.setViewerId(opensocial_viewer_id);
            session.setAppId(opensocial_app_id);
            session.setAccessKey(ss.makeMixiSessionKey());
            Calendar cal = Calendar.getInstance(TimeZoneLocator.get());
            session.setEntryDate(cal.getTime());
            cal.add(Calendar.MINUTE, MixiConstant.MIXI_SESSION_MINUTES());
            session.setExpireDate(cal.getTime());
            Datastore.put(session);
            mixiSessionKey = session.getAccessKey();
        }
        
        XmlGenService xgs = new XmlGenService();
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(false);
        DocumentBuilder builder = factory.newDocumentBuilder();

        Document xml = null;
        
        Velocity.init("WEB-INF/velocity_conf.properties");
        VelocityContext context = new VelocityContext();

        
        String xmlStr = null;
        StringWriter sw = new StringWriter();
        Template template = Velocity.getTemplate("mixi/pc/login.xml", "UTF-8");
        context.put("jsessionid", request.getSession().getId());
        context.put("mixisessionid", mixiSessionKey);
        context.put("mapkey", mapKey);
        context.put("appid", opensocial_app_id);
        context.put("ownerid", opensocial_owner_id);
        context.put("viewerid", opensocial_viewer_id);
        
        template.merge(context, sw);
        xmlStr = sw.toString();
        sw.flush();
        
        InputStream is = new ByteArrayInputStream(xmlStr.getBytes());
        try{
            xml = builder.parse(is);
        }finally{
            is.close();
        }
        
        xgs.printXml(response.getOutputStream(), xml);
        
        return null;
    }
    



}
